#nullable enable

using System;
using System.Collections.Generic;
using PHMEE.CommonUtils;

namespace PHMEE;

/// <summary>
/// 用于处理 Application 层查询时的 DateTime
/// </summary>
public static class QueryDateTimeUtils
{
    /// <summary>
    /// 处理按日期段查询时，endDateTime 加 1 天
    /// </summary>
    /// <param name="startDate"></param>
    /// <param name="endDate"></param>
    /// <returns></returns>
    public static (DateTime startDateTime, DateTime endDateTime) ProcessForDateRangeQuery(DateTime startDate,
        DateTime endDate)
    {
        // 将开始和结束时间都重置为当天的 0 点，防止前端乱传时间
        startDate = startDate.Date;
        endDate = endDate.Date;

        var start = startDate.ToUtcKind();
        var end = endDate.ToUtcKind();
        
        end = end.AddDays(1);
        return (start, end);
    }

    /// <summary>
    /// 处理按日期段查询，endDateTime 加 1 天
    /// </summary>
    /// <param name="startDateTime"></param>
    /// <param name="endDateTime"></param>
    /// <param name="utcNow"></param>
    /// <returns></returns>
    public static (DateTime startDateTime, DateTime endDateTime) ProcessForDateRangeBeforeTodayQuery(DateTime startDateTime, DateTime endDateTime)
    {
        // 将开始和结束时间都重置为当天的0点
        startDateTime = startDateTime.Date;
        endDateTime = endDateTime.Date;

        startDateTime = startDateTime.ToUtcKind();
        endDateTime = endDateTime.ToUtcKind();
        
        // 不再校验是否是未来日期，由前端控制
        // if (end > GetUtcNow().Date)
        // {
        //     throw new UserFriendlyException(_localizer[PHMEEDomainErrorCodes.EndDateMustBeBeforeToday],
        //         PHMEEDomainErrorCodes.EndDateMustBeBeforeToday);
        // }
        
        endDateTime = endDateTime.AddDays(1);
        return (startDateTime, endDateTime);
    }

    /// <summary>
    /// 处理按日查询
    /// </summary>
    /// <param name="date"></param>
    /// <param name="utcNow"></param>
    /// <returns></returns>
    public static (DateTime startDateTime, DateTime endDateTime) ProcessForDayQuery(DateTime? date, DateTime utcNow)
    {
        // 当 date 为 null 时，默认取昨日
        date ??= GetUtcDate(utcNow.AddDays(-1));
        
        // 当 date 有值时，date 取 date 所在日期 0 点
        var preprocessedDate = GetUtcDate(date.Value);

        // 不再校验是否是未来日期，由前端控制        
        // // date 必须小于今日
        // if (preprocessedDate >= GetUtcNow().Date)
        // {
        //     throw new UserFriendlyException(_localizer[PHMEEDomainErrorCodes.DateMustBeBeforeToday],
        //         PHMEEDomainErrorCodes.DateMustBeBeforeToday);
        // }
        
        return (preprocessedDate, preprocessedDate.AddDays(1));
    }

    /// <summary>
    /// 处理按小时查询
    /// </summary>
    /// <param name="hour"></param>
    /// <param name="utcNow"></param>
    /// <returns></returns>
    public static (DateTime startDateTime, DateTime endDateTime) ProcessForHourQuery(DateTime? hour, DateTime utcNow)
    {
        hour ??= GetUtcHour(utcNow.AddHours(-1));
        
        // 当 hour 有值时，hour 取 hour 所在小时 0 分
        var preprocessedHour = GetUtcHour(hour.Value);

        // 不再校验是否是未来小时        
        // // hour 必须小于当前小时
        // if (preprocessedHour >= GetUtcHour(utcNow))
        // {
        //     throw new UserFriendlyException(_localizer[PHMEEDomainErrorCodes.HourMustBeBeforeThisHour],
        //         PHMEEDomainErrorCodes.HourMustBeBeforeThisHour);
        // }
        
        return (preprocessedHour, preprocessedHour.AddHours(1));
    }
    
    private static DateTime GetUtcHour(DateTime dateTime)
    {
        return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, 0, 0, kind: DateTimeKind.Utc);
    }

    /// <summary>
    /// 获取 year 年的 UTC 开始时间
    /// </summary>
    /// <param name="year"></param>
    /// <returns></returns>
    public static DateTime GetUtcYearStartDate(int year)
    {
        return new DateTime(year, 1, 1, 0, 0, 0, kind: DateTimeKind.Utc);
    }

    public static DateTime GetUtcDate(DateTime dateTime)
    {
        var date = dateTime.Date;
        return DateTime.SpecifyKind(date, DateTimeKind.Utc);
    }

    /// <summary>
    /// 获取指定日期的 UTC 日期。如果指定的日期时间是 0 点，则返回前一天的日期。
    /// 这个方法主要用于处理按日期分组的逻辑，当需要将一天的时间定义为从某一天的0点（不含）到第二天的0点（含）时，
    /// 例如，2022年1月1日的数据应该包含从2022年1月1日的0点01分到2022年1月2日的0点的所有数据。
    /// </summary>
    /// <param name="dateTime">需要处理的日期时间</param>
    /// <returns>处理后的 UTC 日期</returns>
    public static DateTime GetOpenCloseUtcDate(DateTime dateTime)
    {
        var date = dateTime.Hour == 0 && dateTime.Minute == 0 ? dateTime.AddDays(-1).Date : dateTime.Date;
        return DateTime.SpecifyKind(date, DateTimeKind.Utc);
    }

    /// <summary>
    /// 生成 now 到 now-2h 之间的时间点列表，间隔是 5 minute，从早到晚顺序排列
    /// </summary>
    /// <param name="now"></param>
    /// <returns></returns>
    public static List<DateTime> GenerateTimePoints(DateTime now)
    {
        List<DateTime> timePoints = [];

        // 将 now 调整为早于自己但离自己最近的 5 分钟的倍数
        int minutesToSubtract = now.Minute % 5;
        DateTime nearestPreviousFiveMinuteMark = now.AddMinutes(-minutesToSubtract);

        // 往前推两个小时
        DateTime startTime = nearestPreviousFiveMinuteMark.AddHours(-2);

        while (startTime <= nearestPreviousFiveMinuteMark)
        {
            timePoints.Add(startTime);
            startTime = startTime.AddMinutes(5); // 每隔 5 分钟
        }

        return timePoints;
    }

    /// <summary>
    /// 获取 startTime 和 endTime 之间的小时数，精确到小数
    /// </summary>
    /// <param name="startTime"></param>
    /// <param name="endTime"></param>
    /// <returns></returns>
    public static float GetHoursBetween(DateTime startTime, DateTime endTime)
    {
        // 计算时间差
        TimeSpan timeDifference = endTime - startTime;

        // 返回小时数
        return (float)timeDifference.TotalHours;
    }

    /// <summary>
    /// 处理按年查询时，endDateTime 为次年 1 月 1 日
    /// </summary>
    /// <param name="yearNumber"></param>
    /// <param name="utcNow"></param>
    /// <returns></returns>
    public static (int yearNumber, DateTime startDateTime, DateTime endDateTime) ProcessForYearQuery(int? yearNumber, DateTime utcNow)
    {
        // 默认本年
        yearNumber ??= utcNow.Year;

        var start = new DateTime(yearNumber.Value, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        var end = new DateTime(yearNumber.Value + 1, 1, 1, 0, 0, 0, DateTimeKind.Utc);

        return (yearNumber.Value, start, end);
    }
    
    public static (int yearNumber, DateTime startDateTime, DateTime endDateTime) ProcessForPastYearQuery(int? yearNumber, DateTime utcNow)
    {
        // 不再校验是否是未来年，由前端控制
        
        // if (yearNumber is not null && yearNumber >= GetUtcNow().Year)
        // {
        //     throw new UserFriendlyException(_localizer[PHMEEDomainErrorCodes.YearMustBeBeforeCurrentYear],
        //         PHMEEDomainErrorCodes.YearMustBeBeforeCurrentYear);
        // }
        
        yearNumber ??= utcNow.Year - 1;
    
        var start = new DateTime(yearNumber.Value, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        var end = new DateTime(yearNumber.Value + 1, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    
        return (yearNumber.Value, start, end);
    }
}